#ifndef INCLUDED_SOCKETS_H
#define INCLUDED_SOCKETS_H

//TO_BE_EXPORTED

#include "bOS.h"

bool BOS_API socketsInit();
void BOS_API socketsEnd();

class BOS_API SocketWrapper
{
	class AcceptedSocket
	{
	private:
		AcceptedSocket(socket_type s, sockaddr_in a);
		AcceptedSocket(const AcceptedSocket &a);
		AcceptedSocket& operator=(const AcceptedSocket &);

		socket_type sock_;
		sockaddr_in addr_;

		friend class SocketWrapper;
	};

public:

	enum sockstate_type
	{
		CLOSED,
		LISTENING,
		ACCEPTED,
		CONNECTED
	};

	SocketWrapper();
	SocketWrapper(const AcceptedSocket &as);
	~SocketWrapper();
	
	//server methods

	// binds and listens on a given port number
	void listen(int port, int backlog = 100);
	AcceptedSocket accept();


	//client methods
	void connect(const char *address, int port);

	// server-client methods
	sockstate_type state() const { return sockstate_; }
	const char * address() const;
	int port() const;

	void write(const void *buf, size_t len);
	size_t read(void *buf, size_t len);

	void close();

private:

	SocketWrapper(const SocketWrapper&);
	SocketWrapper& operator=(const SocketWrapper&);

	socket_type sock_;
	sockaddr_in sockaddress_;
	sockstate_type sockstate_;
};

/*

// this class is supposed to serve as a stream buffer associated with a socket
template <class charT, class traits = std::char_traits<charT> >
class TCPStreamBuffer : public std::basic_streambuf<charT, traits>
{
	typedef std::basic_streambuf<charT, traits> sbuftype;
	typedef typename sbuftype::int_type         int_type;
	typedef charT                               char_type;

public:

	// the buffer will take ownership of the socket (ie. it will close it
	// in the destructor) if takeowner == true
	explicit TCPStreamBuffer(TCPSocketWrapper &sock,
		bool takeowner = false, std::streamsize bufsize = 512)
		: rsocket_(sock), ownsocket_(takeowner),
		inbuf_(NULL), outbuf_(NULL), bufsize_(bufsize),
		remained_(0), ownbuffers_(false)
	{
	}

	~TCPStreamBuffer()
	{
		if (rsocket_.state() == TCPSocketWrapper::CONNECTED ||
			rsocket_.state() == TCPSocketWrapper::ACCEPTED)
			_flush();

		if (ownbuffers_)
		{
			delete [] inbuf_;
			delete [] outbuf_;
		}

		if (ownsocket_ == true)
			rsocket_.close();
	}

protected:
	sbuftype * setbuf(char_type *s, std::streamsize n)
	{
		if (gptr() == NULL)
		{
			setg(s, s + n, s + n);
			setp(s, s + n);
			inbuf_ = s;
			outbuf_ = s;
			bufsize_ = n;
			ownbuffers_ = false;
		}

		return this;
	}

	void _flush()
	{
		rsocket_.write(outbuf_, (pptr() - outbuf_) * sizeof(char_type));
	}

	int_type overflow(int_type c = traits::eof())
	{
		// this method is supposed to flush the put area of the buffer
		// to the I/O device

		// if the buffer was not already allocated nor set by user,
		// do it just now
		if (pptr() == NULL)
		{
			outbuf_ = new char_type[bufsize_];
			ownbuffers_ = true;
		}
		else
		{
			_flush();
		}
		setp(outbuf_, outbuf_ + bufsize_);
		if (c != traits::eof())
			sputc(traits::to_char_type(c));
		return 0;
	}

	int sync()
	{
		// just flush the put area
		_flush();
		setp(outbuf_, outbuf_ + bufsize_);
		return 0;
	}

	int_type underflow()
	{
		// this method is supposed to read some bytes from the I/O device

		// if the buffer was not already allocated nor set by user,
		// do it just now
		if (gptr() == NULL)
		{
			inbuf_ = new char_type[bufsize_];
			ownbuffers_ = true;
		}

		if (remained_ != 0)
			inbuf_[0] = remainedchar_;

		size_t readn = rsocket_.read(static_cast<char*>(inbuf_) + remained_,
			bufsize_ * sizeof(char_type) - remained_);

		// if (readn == 0 && remained_ != 0)
		// error - there is not enough bytes for completing
		// the last character before the end of the stream
		// - this can mean error on the remote end

		if (readn == 0)
			return traits::eof();

		size_t totalbytes = readn + remained_;
		setg(inbuf_, inbuf_,
			inbuf_ + totalbytes / sizeof(char_type));

		remained_ = totalbytes % sizeof(char_type);
		if (remained_ != 0)
			remainedchar_ = inbuf_[totalbytes / sizeof(char_type)];

		return sgetc();
	}

private:

	// not for use
	TCPStreamBuffer(const TCPStreamBuffer&);
	TCPStreamBuffer& operator=(const TCPStreamBuffer&);

	TCPSocketWrapper &rsocket_;
	bool ownsocket_;
	char_type *inbuf_;
	char_type *outbuf_;
	std::streamsize bufsize_;
	size_t remained_;
	char_type remainedchar_;
	bool ownbuffers_;
};

// this class is an ultimate stream associated with a socket
template <class charT, class traits = std::char_traits<charT> >
class TCPGenericStream :
	private TCPStreamBuffer<charT, traits>,
	public std::basic_iostream<charT, traits>
{
public:

	// this constructor takes 'ownership' of the socket wrapper if btakeowner == true,
	// so that the socket will be closed in the destructor of the
	// TCPStreamBuffer object
	explicit TCPGenericStream(TCPSocketWrapper &sock, bool takeowner = false)
		: TCPStreamBuffer<charT, traits>(sock, takeowner),
		std::basic_iostream<charT, traits>(this)
	{
	}

private:
	// not for use
	TCPGenericStream(const TCPGenericStream&);
	TCPGenericStream& operator=(const TCPGenericStream&);
};

// this is even more specialized for use as a client
template <class charT, class traits = std::char_traits<charT> >
class TCPGenericClientStream :
	private TCPSocketWrapper,
	public TCPGenericStream<charT, traits>
{
public:

	TCPGenericClientStream(const char *address, int port)
		: TCPGenericStream<charT, traits>(*this, false)
	{
		TCPSocketWrapper::connect(address, port);
	}

private:
	// not for use
	TCPGenericClientStream(const TCPGenericClientStream&);
	TCPGenericClientStream& operator=(const TCPGenericClientStream&);
};

// helper declarations for narrow and wide streams
typedef TCPGenericStream<char> TCPStream;
typedef TCPGenericStream<wchar_t> TCPWStream;
typedef TCPGenericClientStream<char> TCPClientStream;
typedef TCPGenericClientStream<wchar_t> TCPWClientStream;

*/


#endif
